package top.appx.espring.filter;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
import org.apache.log4j.Logger;
import top.appx.espring.InitMethod;
import top.appx.espring.Utils;
import top.appx.espring.annotation.Action;
import top.appx.espring.annotation.Controller;
import top.appx.espring.annotation.RequestParam;
import top.appx.espring.ex.ESpringException;
import top.appx.espring.vo.ResultVO;
import top.appx.zutil.StringUtil;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import static top.appx.espring.InitMethod.controllerList;
import static top.appx.espring.InitMethod.sourceMap;
import static top.appx.espring.vo.ResultVO.error;
import static top.appx.espring.vo.ResultVO.success;

/**
 * Created by john on 2015/12/10.
 */
@WebFilter("/*")
public class MainFilter implements Filter {
    Logger Log=Logger.getLogger(MainFilter.class.getName());

    private static SerializeConfig mapping = new SerializeConfig();
    static {
        mapping.put(Timestamp.class, new SimpleDateFormatSerializer("yyyy-MM-dd HH:mm:ss"));
    }


    public void init(FilterConfig filterConfig) throws ServletException {
        Log.debug("espring启动");
        try {
            InitMethod.init();
        } catch (Exception e) {
            Log.debug("初始化出错",e);
        }
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)servletRequest;
        HttpServletResponse resp = (HttpServletResponse)servletResponse;

        String path = req.getServletPath();
        if(path.contains(".") && !path.endsWith(".do")){
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }

        if(path.contains(".")){
            path = path.substring(0,path.indexOf("."));
        }

        String action = req.getParameter("action");
        Method m = null;
        List<Object> paramValues = new ArrayList<Object>();
        Class c = null;
        Object obj = null;
        for(Object obj1 :controllerList){
            Class c1 = obj1.getClass();
            if(c1.isAnnotationPresent(Controller.class)){
                Controller controller = (Controller)c1.getAnnotation(Controller.class);
                for(String str:controller.value()){
                    if(str.equals(path)){
                        c = c1;
                        obj = obj1;
                        break;
                    }
                    if(c!=null){
                        break;
                    }
                }
            }
        }
        if(c == null){
            System.out.println("doFilterChain");
            filterChain.doFilter(servletRequest,servletResponse);
            return;
        }

        boolean isExist = c.isAnnotationPresent(Controller.class);
        if(isExist){
            Method[] methods = c.getMethods();
            for(Method method1 : methods){
                if(method1.isAnnotationPresent(Action.class)){
                    Action action1 = (Action)method1.getAnnotation(Action.class);
                    if(action1.value().equals(action)){
                        m = method1;
                        break;
                    }
                }else if(method1.getName().equals(action)){
                    m = method1;
                }
            }
        }

        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        PrintWriter out = resp.getWriter();

        if(m == null){
            out.print(JSONObject.toJSONString(error("not found the action:"+action+" by "+path)));
            return;
        }
        Annotation[][] anss = m.getParameterAnnotations();
        Class<?>[] typs = m.getParameterTypes();
        for(int i=0;i<typs.length;i++){
            Class c1 = typs[i];
            Log.debug("参数:" + c1);

            if (c1.equals(HttpServletRequest.class)) {
                paramValues.add(req);
            } else if (c1.equals(HttpServletResponse.class)) {
                paramValues.add(resp);
            } else if (c1.equals(HttpSession.class)) {
                paramValues.add(req.getSession(true));
            } else {

                String parmName = "";
                boolean required = false;

                Annotation[] ans = anss[i];
                for(Annotation an : ans){
                    if(an.annotationType().equals(RequestParam.class)){
                        RequestParam requestParam = (RequestParam)an;
                        parmName = requestParam.value();
                        required = requestParam.required();
                    }
                }

                //region 获取参数名称
                if (StringUtil.isNullOrEmpaty(parmName)) {

                }
                //endregion 获取参数名称

                System.out.println("参数名称:"+parmName);

                if (c1.equals(String.class) || c1.equals(int.class) || c1.equals(Integer.class)) {
                    String pv = req.getParameter(parmName);
                    if (required && pv == null) {
                        throw new ESpringException("必须提供参数 '" + parmName + "'");
                    }

                    if (c1.equals(String.class)) {
                        paramValues.add(pv);
                    } else if (c1.equals(int.class)) {
                        if (pv == null) {
                            paramValues.add(0);
                        } else {
                            paramValues.add(Integer.parseInt(pv));
                        }
                    } else if (c1.equals(Integer.class)) {
                        if (pv == null) {
                            paramValues.add(null);
                        } else {
                            paramValues.add(Integer.parseInt(parmName));
                        }
                    }
                } else {
                    Log.debug("识别为entity:::" + c1);
                    try {
                        Method[] methods = c1.getMethods();
                        Object entity = c1.newInstance();
                        for (Method method : methods) {
                            System.out.println(method);
                            if (!method.getName().startsWith("set") || method.getParameterTypes().length != 1) {
                                continue;
                            }
                            String paramName = method.getName().substring(3);
                            System.out.println(paramName);
                            paramName = (paramName.charAt(0) + "").toLowerCase() + paramName.substring(1);

                            String valueStr = req.getParameter(paramName);
                            if (valueStr == null) {
                                continue;
                            }
                            Object value = null;
                            Class<?> t = method.getParameterTypes()[0];
                            if (t.equals(String.class)) {
                                value = req.getParameter(paramName);
                            } else if (t.equals(Integer.class) || t.equals(int.class)) {
                                if (valueStr.length() != 0) {
                                    value = Integer.parseInt(valueStr);
                                }
                            } else if (t.equals(Timestamp.class)) {
                                if (valueStr.length() != 0) {
                                    value = Timestamp.parse(valueStr);
                                }
                            }
                            if (value != null) {
                                method.invoke(entity, value);
                            }
                        }
                        paramValues.add(entity);
                    } catch (Exception ex) {
                        throw new ESpringException(ex);
                    }

                }
            }
        }
        try {
            Object obj2 = m.invoke(obj, paramValues.toArray());
            Class rt = m.getReturnType();
            if(rt.getName().equals("void")){
                out.print(JSONObject.toJSONString(success()));
            }
            else if(rt.equals(String.class)){
                resp.sendRedirect(obj2.toString());
            }
            else{
                if(obj2 != null){
                    String str = JSONObject.toJSONString(obj2,mapping);
                    out.print(str);
                }
            }

        }catch (IllegalAccessException e) {
            throw new ServletException(e);
        } catch (InvocationTargetException e) {
            if(e.getTargetException() instanceof  ESpringException){
                out.print(JSONObject.toJSONString(error(e.getTargetException().getMessage())));
            }else{
                throw new ServletException(e);
            }
        }




    }

    public void destroy() {
        System.out.println("destroy");
    }
}
